{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How fast are NumPy operations compared to regular Python math?\n",
    "#### In this brief notebook, we will create a list of 1-million random floating point numbers. Then we will use *for* loop to iterate over its elements, take Log10 and store the value in another list. We'll compare the execution speed with that of a direct NumPy Log10 operation\n",
    "\n",
    "You will often come across this assertion in the data science and Python community that NumPy is much faster due to its vectorized implementation and due to the fact that many of its core routines are written in C (based on CPython framework).\n",
    "\n",
    "And it is indeed true. \n",
    "\n",
    "Numpy arrays are densely packed arrays of homogeneous type. Python lists, by contrast, are arrays of pointers to objects, even when all of them are of the same type. So, you get the benefits of locality of reference. Also, many Numpy operations are implemented in C, avoiding the general cost of loops in Python, pointer indirection and per-element dynamic type checking. The speed boost depends on which operations you’re performing. \n",
    "\n",
    "For data science and modern machine learning tasks, this is an invaluable advantage, as often the data set size runs into millions if not billions of records."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from math import log10 as lg10\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "import random\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "N = 1000000 # Number of records to process\n",
    "speed = [] # Empty list to store operation speeds (time taken)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Create a list of 1 million numbers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Length of l1: 1000000\n"
     ]
    }
   ],
   "source": [
    "l1 = list(100*(np.random.random(N))+1)\n",
    "print(\"Length of l1:\",len(l1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First few elements of the array: [30.848911201460105, 4.0880342139352646, 70.039804881231177, 90.752231323733639]\n"
     ]
    }
   ],
   "source": [
    "print(\"First few elements of the array:\", l1[:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Create a NumPy ndarray object from that list i.e. vectorize it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "a1 = np.array(l1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape of a1 object: (1000000,)\n",
      "Type of a1 object: <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "print(\"Shape of a1 object:\",a1.shape)\n",
    "print(\"Type of a1 object:\",type(a1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Create a blank list for appending elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "l2=[] # Just a blanck list to append to"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How fast is - For loop and appending"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With for loop and appending it took 0.3542044162750244 seconds\n"
     ]
    }
   ],
   "source": [
    "t1=time.time()\n",
    "for item in l1:\n",
    "    l2.append(lg10(item))\n",
    "t2 = time.time()\n",
    "print(\"With for loop and appending it took {} seconds\".format(t2-t1))\n",
    "speed.append(t2-t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First few elements of the resulting array: [1.4892398404100269, 0.6115145218774171, 1.8453449275438665, 1.9578573118312546]\n"
     ]
    }
   ],
   "source": [
    "print(\"First few elements of the resulting array:\", l2[:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How fast it - List comprehension"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With list comprehension, it took 0.16927814483642578 seconds\n"
     ]
    }
   ],
   "source": [
    "t1=time.time()\n",
    "l2 = [lg10(i) for i in range(1,1000001)]\n",
    "t2 = time.time()\n",
    "print(\"With list comprehension, it took {} seconds\".format(t2-t1))\n",
    "speed.append(t2-t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First few elements of the resulting array: [0.0, 0.3010299956639812, 0.47712125471966244, 0.6020599913279624]\n"
     ]
    }
   ],
   "source": [
    "print(\"First few elements of the resulting array:\", l2[:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How fast is - *Map* function method"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def op1(x):\n",
    "    return (lg10(x))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With map functional method it took 0.2851881980895996 seconds\n"
     ]
    }
   ],
   "source": [
    "t1=time.time()\n",
    "l2=list(map(op1,l1))\n",
    "t2 = time.time()\n",
    "print(\"With map functional method it took {} seconds\".format(t2-t1))\n",
    "speed.append(t2-t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First few elements of the resulting array: [1.4892398404100269, 0.6115145218774171, 1.8453449275438665, 1.9578573118312546]\n"
     ]
    }
   ],
   "source": [
    "print(\"First few elements of the resulting array:\", l2[:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### How fast is - NumPy operation (vectorized array)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With direct NumPy log10 method it took 0.0312502384185791 seconds\n"
     ]
    }
   ],
   "source": [
    "t1=time.time()\n",
    "a2=np.log10(a1)\n",
    "t2 = time.time()\n",
    "print(\"With direct NumPy log10 method it took {} seconds\".format(t2-t1))\n",
    "speed.append(t2-t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First few elements of the resulting array: [1.4892398404100269, 0.61151452187741706, 1.8453449275438665, 1.9578573118312546]\n"
     ]
    }
   ],
   "source": [
    "l3 = list(a2)\n",
    "print(\"First few elements of the resulting array:\", l3[:4])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Plot the time taken by each operation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Container object of 4 artists>"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\nzXHAYVV1YV/5g1h2s8J+feu6fNa9FgLbJVmr7abscg3rj2i7sNNct/ggxq/Xnb63NFhtj8w3aJK3\npRYCT26X/5ZmHvHJ8iXgq9ja1omJ2/D6DLBWkguBrwP7tc3YK+MVNNciXUBzfcl7JigHuD3J/9Jc\nn9X7S7+me0CSRT2P3mvCZgFfbj+r/wU+UVU3AycBL8goNyfQXKP2kDQX758P7NzO7XsI8GPgfOC8\nqvruSsZ5NvCfNF23/1lV5/ZvUFW/o0kCvtbWgTOBiW4AeC/NF/0FSS5un49nI2CkbQn+MvC2vhhu\np+lW+Wb7vt1DU+fWSFW1qKo+McqqDwHvb38n+1uoJvys+/wcuIqmm/ITwP90CO3/ATu3n9F5wDZV\ndSNNN/tFST7ct/1kfG9pchwB9A7t83mafwzPB57O8q3wq+srwENYscVYo3DmBE2aJCPAWzr8AdB9\nUJL9gHlVddB0x6LB8rPWfUl7t+qeVfXy6Y5lJvAaAUmSNC2SfBLYHfjr6Y5lprDFTZIkaYbwGjdJ\nkqQZwsRNkiRphjBxkyRJmiFM3CQtJ8mXk3xnuuMYJu3YZ59PcmOSasc5HApJdmlfk7OkSFPAxE2a\nQZKcmORHY6x7XPsHdNfVPM3rWHGw1imV5NVJbp7OGCbZ39IMErwH8EiWn090xmjHIHxjX/EZNK/J\nwbalKWDiJs0sx9IMaLr5KOv2p5nb8YercuAk94Nm1ox2AGBNnq2A66rqrKr6zX1pEu00VnkU/Kq6\ns31NDlEgTQETN2lmORn4LcsmZQbuTbpeDhzXzj9Kko8kuTzJbUmuSvKBJOv27HN4OyvD/kmupJn1\n4v79XaVt2SeS3JDk9iRnJtmxZ/0KXWVJtmrLtmufr5PkU0muT3JHkmuT/NtoLzDJLjSjtD+oPUYl\neUeS94w2p26S/07y0Xb5y0m+k+Tdbbx/SnJMkvv3bL9WkrclubJ9by5MsnfP+iQ5NMnVbazXJxl3\nKp4k85Oc3b4/v2nf+3WWxgR8GNiyfS0LVuU47fqfte/jp5LcnOSm9nNdq2ebdZN8OMl1SW5tj7fL\nKJ/XbknOpZlX8jlJtm5bdH+bZHGS85Ls3ntumhktPtbuv6TveL2f/4vSzIxwR5JrkhySNNOdtesX\ntZ/BMUluaetD72wiJHltkv9rj/G7JKf2vk5pjVVVPnz4mEEP4AM0LWtr9ZS9kGbi7U16yt4F7Ahs\n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\nRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1d8bcb6b198>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,6))\n",
    "plt.ylabel(\"Time taken to process 1 million records in seconds\",fontsize=12)\n",
    "plt.xlabel(\"Various types of operations\",fontsize=14)\n",
    "plt.grid(True)\n",
    "plt.bar(left=[1,2,3,4],height=speed, align='center',tick_label=['For-loop','List comprehension','Map function','NumPy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Therefore, we see the evidence that NumPy operations over ndarray objects are much faster than regular Python math operations over corresponding list. The exact speed of regular Python operations vary a little but they are always much slower compared to the vectorized NumPy operation."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
